﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Secams.controller;
using Secams.model;
using Secams.utils;
using Secams.form;

namespace Secams
{
    public partial class FormEventsGrid : Form, EventListener
    {
        public enum SimulStatus { init, running, decided, confirmed };

        const string BTN_START = "Start";
        const string BTN_DECIDE = "Decide";
        const string BTN_CONFIRM = "Confirm Threat Events";

        Color COLOR_HIT = Color.Green;
        Color COLOR_CR = Color.LightGreen;
        Color COLOR_FA = Color.Orange;
        Color COLOR_MISS = Color.Red;

        const int DECISION_ONLY_AT_END = 1;
        const int DECISION_FORCE_AT_END = 2;

        MainForm _mainForm = null;

        // Table rendering
        DataSchema _tableSchema = null;
        ColumnHeader[] _columnHeaders = null;


        // Whether to allow decision confirmation (or just proceed)
        protected bool _allowDecisionConf = false;
        protected int _decisionOption = DECISION_FORCE_AT_END | DECISION_ONLY_AT_END; // force decision at the end of trial

        protected SimulStatus _decisionStatus = SimulStatus.init;

        int _actionCtr = 0;

        ActionTraceDao _actionTraceDao = new ActionTraceDao(AppContext.getInstance().dbFilePath);
        EventTraceDao _eventTraceDao = new EventTraceDao(AppContext.getInstance().dbFilePath);
        ParticipantDao _participantDao = new ParticipantDao(AppContext.getInstance().dbFilePath);

        EventMonitorSimulator ems = new EventMonitorSimulator();

        ParticipantInfo _participantInfo;
        Trials _trials;
        TrialProfile _currentTrial;



        public FormEventsGrid(MainForm mainForm, ScenarioInstanceSequence scenarioSeq, Trials trials)
        {
            _mainForm = mainForm;

            _trials = trials;

            this.Font = AppContext.getDefaultFont(this.Font);
            InitializeComponent();

            // hide the score until a train trial is complete
            this.grpScorePanel.Visible = false;
            this.pnlDetailScore.Visible = false;
            this.txtScoreHit.BackColor = COLOR_HIT;
            this.txtScoreMiss.BackColor = COLOR_MISS;
            this.txtScoreFa.BackColor = COLOR_FA;
            this.txtScoreCr.BackColor = COLOR_CR;

            btnMakeDecision.Text = BTN_START;
            lstEvents.CheckBoxes = true;

            ems.setScenarioSeq(scenarioSeq);

            tabAlerts.Hide();

            _tableSchema = new DataSchema();
            _tableSchema.addField("id", typeof(string), "ID");
            _tableSchema.getField("id").setAttribute("width", 70);
            _tableSchema.addField("signature", typeof(string), "Alert");
            _tableSchema.getField("signature").setAttribute("width", 330);
            _tableSchema.addField("description", typeof(string), "Description");
            _tableSchema.getField("description").setAttribute("width", 500);

            _columnHeaders = new ColumnHeader[_tableSchema.size()];

            int i= 0;
            foreach (DataField field in _tableSchema.fields)
            {
                _columnHeaders[i] = new ColumnHeader();
                _columnHeaders[i].Text = field.header;
                _columnHeaders[i].Width = (int)field.getAttribute("width");
                i++;
            }

            // Data grid columns
            this.dgridEvents.AllowUserToAddRows = false;

            for (i = 0; i < this.dgridEvents.Columns.Count; i++ )
                this.dgridEvents.Columns.RemoveAt(i);

            DataGridViewCheckBoxColumn newCol;
            newCol = new DataGridViewCheckBoxColumn(); // add a check box column to the grid
            newCol.HeaderText = "Is threat";
            newCol.Name = "IsThreat";
            newCol.Visible = true;
            newCol.Width = 60;
            dgridEvents.Columns.Add(newCol);

            foreach (DataField field in _tableSchema.fields)
            {
                DataGridViewTextBoxColumn textCol = new DataGridViewTextBoxColumn(); // add a column to the grid
                textCol.HeaderText = field.header;
                textCol.Name = field.fieldName;
                textCol.Visible = true;
                textCol.Width = (int)field.getAttribute("width");
                textCol.DefaultCellStyle.WrapMode =  DataGridViewTriState.True;
                dgridEvents.Columns.Add(textCol);
            }


        }

        public void setParticipantInfo(ParticipantInfo participantInfo)
        {
            this.txtParticipantName.Text = participantInfo.id;
            _participantInfo = participantInfo;
        }

        public bool hasDecisionOption(int option)
        {
            return (_decisionOption & option) > 0;
        }

        delegate void ClearEventsPaneCallback(bool showMessage);

        protected void clearEventsPane(bool showMessage)
        {

            if (this.dgridEvents.InvokeRequired)
            {
                ClearEventsPaneCallback d = new ClearEventsPaneCallback(clearEventsPane);
                this.Invoke(d, new object[] { showMessage });
            }
            else
            {
                if (showMessage)
                {
                    DialogResult result = MessageBox.Show(this, "Starting " + _currentTrial.getModeText() + " Trial " + _currentTrial.trial.trialNum + ".", "Confirmation");
                }
            
                this.grpScorePanel.Visible = false;
                this.pnlDetailScore.Visible = false;
                this.lstEvents.Clear();

                this.lstEvents.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
                    this.columnHeader1,
                    this.columnHeader2,
                    this.columnHeader3,
                    this.columnHeader4,
                    this.columnHeader5,
                    this.columnHeader6,
                    this.columnHeader7});

                //this.dgridEvents.Clear();
                while (dgridEvents.Rows.Count > 0)
                {
                    this.dgridEvents.Rows.RemoveAt(0);
                }
            }

            
        }



        private void FormEvents_Load(object sender, EventArgs e)
        {
            this.MinimizeBox = false;
            this.ShowIcon = false;

            ems.registerListener(this);

            clearEventsPane(false);

            this.txtScearioSeqName.Text = ems.scenarioSeq.name;
        }

        private void rdoRealTime_CheckedChanged(object sender, EventArgs e)
        {
            //if (rdoRealTime.Checked)
        }

        private void btnMakeDecision_Click(object sender, EventArgs e)
        {
            if (_decisionStatus == SimulStatus.init)
            {
                statusActionRun();
            }
            else if (_decisionStatus == SimulStatus.running)
            {
                statusActionDecide();
            }
            else if (_decisionStatus == SimulStatus.decided)
            {
                statusActionConfirm();
            }
        }

        private void statusActionRun()
        {
            clearEventsPane(false);

            if (hasDecisionOption(DECISION_ONLY_AT_END))
            {
                _decisionStatus = SimulStatus.running;
                btnMakeDecision.Text = "";
                btnMakeDecision.Enabled = false;
            }
            else
            {
                _decisionStatus = SimulStatus.running;
                btnMakeDecision.Text = BTN_DECIDE;
            }

            ems.startThread();
        }

        private void statusActionDecide()
        {
            // handle stop
            EventMonitorSimulator.pauseThread();
            //MessageBox.Show("Thread decision", "Threat detected", MessageBoxButtons.OK);
            NiEvent lastEvent = ems.getCurrentEvent();

            FormDecision frmDecision = new FormDecision(true, lastEvent);
            frmDecision.StartPosition = FormStartPosition.CenterParent;
            DialogResult res = frmDecision.ShowDialog(this);
            if (res == DialogResult.OK)
            {
                // Accepted
                //ems.stopThread();
                EventMonitorSimulator.pauseThread();
                if (_allowDecisionConf)
                {
                    btnMakeDecision.Text = BTN_CONFIRM;
                    _decisionStatus = SimulStatus.decided;
                }
                else
                {
                    _currentTrial.decided = true;
                    _currentTrial.decision = frmDecision.getDecision();
                    _currentTrial.decisionReason = frmDecision.getReason();
                    _currentTrial.decisionConfidence = frmDecision.getConfidence();
                    traceAction("Decision", "Decision=" + _currentTrial.decision, "Confidence=" + _currentTrial.decisionConfidence.ToString(), "Reason=" + _currentTrial.decisionReason);
                    statusActionConfirm();

                    EventMonitorSimulator.resumeThread();
                }
            }
            else
            {
                // Canceled, resume
                EventMonitorSimulator.resumeThread();
                btnMakeDecision.Text = BTN_DECIDE;
                _decisionStatus = SimulStatus.running;
            }
        }

        private void statusActionConfirm()
        {
            // Check for threat events
            StringBuilder msg = new StringBuilder("Checked events: ");

            // DEBUG:
            if (AppContext.getInstance().traceLevel > 0)
            {
                foreach (DataGridViewRow row in this.dgridEvents.Rows)
                {
                    DataGridViewCheckBoxCell checkBoxCell = (DataGridViewCheckBoxCell)row.Cells[0];
                    if ( (bool)checkBoxCell.Value)
                        msg.Append(row.Cells[1].Value.ToString());
                }
                DialogResult result = MessageBox.Show(this, msg.ToString(), "Confirmation");
            }


            btnMakeDecision.Text = BTN_DECIDE;
            _decisionStatus = SimulStatus.running;

            // breaks the current scenario, and move on to the next
            ems.breakScenario();
        }

        // For multithread access
        delegate void UpdateTrialNumCallback();

        protected void updateTrialNum()
        {

            if (this.txtTrialNum.InvokeRequired)
            {
                UpdateTrialNumCallback d = new UpdateTrialNumCallback(updateTrialNum);
                this.Invoke(d);
            }
            else
            {
                this.txtTrialNum.Text = _currentTrial.trial.trialNum.ToString();
            }
        }

        // For multithread access
        delegate void EnableAlertListCallback(bool enable);

        private void enableAlertList(bool enable)
        {
            if (this.dgridEvents.InvokeRequired)
            {
                EnableAlertListCallback d = new EnableAlertListCallback(enableAlertList);
                this.Invoke(d, new object[] { enable });
            }
            else
            {
                this.dgridEvents.Enabled = enable;
            }
        }

        // For multithread access
        delegate void HandleEndTrialCallback();

        /// <summary>
        /// End of trial: show feedback
        /// 0: No feedback
        /// 1: Only total
        /// 2: Breakdown by subtotals of Hit, Miss, FA, CR
        /// 3: 2+Coloring of events
        /// </summary>
        private void handleEndTrial()
        {
            if (this.dgridEvents.InvokeRequired)
            {
                HandleEndTrialCallback d = new HandleEndTrialCallback(handleEndTrial);
                this.Invoke(d);
            }
            else
            {
                // Decision Dialog
                if (hasDecisionOption(DECISION_FORCE_AT_END) && !_currentTrial.decided)
                {
                    FormDecision frmDecision = new FormDecision(false, ems.getCurrentEvent());
                    frmDecision.StartPosition = FormStartPosition.CenterScreen;
                    DialogResult res = frmDecision.ShowDialog(this);
                    _currentTrial.decided = true;
                    _currentTrial.decision = frmDecision.getDecision();
                    _currentTrial.decisionReason = frmDecision.getReason();
                    _currentTrial.decisionConfidence = frmDecision.getConfidence();
                    traceAction("Decision", "Decision=" + _currentTrial.decision, "Confidence=" + _currentTrial.decisionConfidence.ToString(), "Reason=" + _currentTrial.decisionReason);
                }


                int scoreHit = 0;
                int scoreMiss = 0;
                int scoreFa = 0;
                int scoreCr = 0;

                // Score ++ when isTreatch matches, -- otherwise
                int trialScore = 0;
                ScenarioInstance currScenarioInstance = ems.getCurrentScenarioInstance();
                Scenario currScenario = currScenarioInstance.scenario;

                for (int i = 0; i < this.dgridEvents.Rows.Count; i++)
                {
                    // mark in the data & save in the db
                    EventProfile evtProfile = _currentTrial.trial.eventProfiles[i];
                    DataGridViewRow currRow = this.dgridEvents.Rows[i];
                    DataGridViewCheckBoxCell checkBoxCell = (DataGridViewCheckBoxCell)currRow.Cells[0];
                    evtProfile.labeledAsThreath = (bool)checkBoxCell.Value;
                    evtProfile.remark = "";

                    TraceEvent traceEvent = new TraceEvent(evtProfile);
                    traceEvent.participantId = _participantInfo.id;
                    traceEvent.scenarioOrder = _currentTrial.scenarioName;
                    traceEvent.scenarioVersion = ems.scenarioSeq.name;
                    traceEvent.trialNum = _currentTrial.trial.trialNum;
                    _eventTraceDao.insert(traceEvent);


                    // Mark column
                    if (currScenarioInstance.scenario.events[i].isThreat)
                    {
                        if (evtProfile.labeledAsThreath)
                        {
                            // HIT
                            if (currScenarioInstance.feedbackLevel >= 3)
                                this.dgridEvents.Rows[i].Cells[0].Style.BackColor = COLOR_HIT;
                            scoreHit += currScenario.scoring.pointHit;
                        }
                        else
                        {
                            // MISS
                            if (currScenarioInstance.feedbackLevel >= 3)
                                this.dgridEvents.Rows[i].Cells[0].Style.BackColor = COLOR_MISS;
                            scoreMiss += currScenario.scoring.pointMiss;
                        }
                    }
                    else
                    {
                        if (evtProfile.labeledAsThreath)
                        {
                            // FALSE ALARM
                            if (currScenarioInstance.feedbackLevel >= 3)
                                this.dgridEvents.Rows[i].Cells[0].Style.BackColor = COLOR_FA;
                            scoreFa += currScenario.scoring.pointFa;
                        }
                        else
                        {
                            // CORRECT REJECTION
                            if (currScenarioInstance.feedbackLevel >= 3)
                                this.dgridEvents.Rows[i].Cells[0].Style.BackColor = COLOR_CR;
                            scoreCr += currScenario.scoring.pointCr;
                        }
                    }
                }

                // update score
                trialScore = scoreHit + scoreMiss + scoreFa + scoreCr;
                if (currScenarioInstance.mode == TrialProfile.MODE_TRAINING)
                {
                    _participantInfo.scoreTrain += trialScore;
                } else {
                    _participantInfo.scoreTransfer += trialScore;
                }
                _participantDao.updateScore(_participantInfo);

                //if (ems.getCurrentScenarioMode() == TrialProfile.MODE_TRAINING)
                if (currScenarioInstance.feedbackLevel >= 1)
                {
                    this.grpScorePanel.Visible = true;

                    if (currScenarioInstance.feedbackLevel >= 2)
                        this.pnlDetailScore.Visible = true;

                    this.txtScoreHit.Text = scoreHit.ToString();
                    this.txtScoreMiss.Text = scoreMiss.ToString();
                    this.txtScoreFa.Text = scoreFa.ToString();
                    this.txtScoreCr.Text = scoreCr.ToString();

                    this.lblWeightHit.Text = currScenario.scoring.pointHit.ToString();
                    this.lblWeightMiss.Text = currScenario.scoring.pointMiss.ToString();
                    this.lblWeightFa.Text = currScenario.scoring.pointFa.ToString();
                    this.lblWeightCr.Text = currScenario.scoring.pointCr.ToString();

                    this.txtScoreTotal.Text = trialScore.ToString();

                    int grandTotal = _participantInfo.scoreTrain + _participantInfo.scoreTransfer;
                    this.txtScoreTotalCummul.Text = grandTotal.ToString();

                    DialogResult result = MessageBox.Show(this, "Train trial Finished. Your score is " + trialScore, "Confirmation");
                }
                else
                {
                    DialogResult result = MessageBox.Show(this, "Transfer trial " + _currentTrial.trial.trialNum.ToString() +" finished.", "Confirmation");
                }
            }
        }

        // For multithread access
        delegate void TermianteSimulCallback();

        private void terminateSimul()
        {
            if (this.dgridEvents.InvokeRequired)
            {
                TermianteSimulCallback d = new TermianteSimulCallback(terminateSimul);
                this.Invoke(d);
            }
            else
            {
                //DialogResult result = MessageBox.Show(this, "Finished", "Confirmation");
                FormSimulCompleted formSimulCompleted = new FormSimulCompleted(_participantInfo);
                formSimulCompleted.StartPosition = FormStartPosition.CenterParent;
                DialogResult result = formSimulCompleted.ShowDialog();

                if (result == System.Windows.Forms.DialogResult.OK) 
                    _mainForm.adminMode(true);
            }

            btnMakeDecision.Text = "DONE";
            btnMakeDecision.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
            btnMakeDecision.Enabled = false;
        }


        delegate void AddEventCallback(NiEvent niEvent);
        
        /// <summary>
        /// Adds an event to the List widget
        /// </summary>
        /// <param name="niEvent"></param>
        private void addEvent(NiEvent niEvent)
        {

            if (this.dgridEvents.InvokeRequired)
            {
                AddEventCallback d = new AddEventCallback(addEvent);
                this.Invoke(d, new object[] { niEvent });
            }
            else
            {
                DataGridViewCell[] cells = new DataGridViewCell[4];
                cells[0] = new DataGridViewCheckBoxCell();
                cells[0].Value = false;
                cells[1] = new DataGridViewTextBoxCell();
                cells[1].Value = niEvent.id;
                cells[2] = new DataGridViewTextBoxCell();
                cells[2].Value = niEvent.signature;
                cells[3] = new DataGridViewTextBoxCell();
                cells[3].Value = niEvent.description;
                DataGridViewRow row = new DataGridViewRow();
                row.Cells.AddRange(cells);
                row.Height = row.Height * 2;
                this.dgridEvents.Rows.Add(row);
                cells[1].ReadOnly = true;
                cells[2].ReadOnly = true;
                cells[3].ReadOnly = true;
                row.Visible = true;

                this.dgridEvents.FirstDisplayedScrollingRowIndex = this.dgridEvents.Rows.Count - 1;
            }
        }

        #region EventListener Callback Operations
        // EventListener interface
        public void onNewEvent(NiEvent niEvent)
        {
            addEvent(niEvent);
            // Accumulating to the trial
            _currentTrial.trial.addEvent(niEvent);
        }

        // EventListener interface
        public void onNewTrial(int trialNum, string scenarioName, int mode)
        {
            // create new trial
            _currentTrial = _trials.addTrial(_participantInfo, ems.scenarioSeq.name, scenarioName, mode);
            updateTrialNum();
            clearEventsPane(true);
        }

        // EventListener interface
        public void onEndTrial(int trialNum, string scenarioName)
        {
            handleEndTrial();
        }

        public void onFinished()
        {
            enableAlertList(false);
            terminateSimul();
        }
        #endregion


        void traceAction(string actionElement, string actionPerformed, string actionDescr, string remark)
        {
            TraceAction action = new TraceAction();
            action.participantId = _currentTrial.participant.id;
            action.scenarioVersion = ems.scenarioSeq.name;
            action.scenarioOrder = _currentTrial.scenarioName;
            action.trialNum = _currentTrial.trial.trialNum;
            action.actionNumber = ++_actionCtr;
            action.actionElement = actionElement;
            action.actionPerformed = actionPerformed;
            action.actionDescription = actionDescr;
            action.registDt = DateTime.Now;
            action.remark = remark;
            _actionTraceDao.insert(action);
        }

        private void dgridEvents_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            if (dgridEvents.CurrentCell is DataGridViewCheckBoxCell)
            {
                DataGridViewCheckBoxCell checkBoxCell = (DataGridViewCheckBoxCell)dgridEvents.CurrentCell;
                string actionElement = checkBoxCell.RowIndex.ToString();
                string actionPerformed = ( (bool)checkBoxCell.Value ) ? "Checked" : "UnChecked";
                traceAction(
                    actionElement, actionPerformed, actionElement + " " + actionPerformed
                    , "");
            }
        }


    }
}
